package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.heima.article.dto.ArticleDto;
import com.heima.article.dto.ArticleHomeDto;
import com.heima.article.dto.ArticleInfoDto;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApArticleContent;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.IApArticleContentService;
import com.heima.article.service.IApArticleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.task.ArticleHtmlTasks;
import com.heima.common.dto.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import org.checkerframework.checker.units.qual.A;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author ZC
 * @since 2022-06-30
 */
@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements IApArticleService {
    @Autowired
    private IApArticleContentService apArticleContentService;
    @Autowired
    private ArticleHtmlTasks articleHtmlTasks;

    @Override
    public ResponseResult saveApArticle(ArticleDto dto) {
        if (dto.getId() != null) {
            //修改的操作
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto, apArticle);
            apArticle.setCreatedTime(new Date());
            //修改文章的信息
            this.updateById(apArticle);
            //修改文章的内容
            LambdaUpdateWrapper<ApArticleContent> updateWrapper = new LambdaUpdateWrapper();
            updateWrapper.eq(ApArticleContent::getArticleId, apArticle.getId());// 对应的是where后面的内容
            updateWrapper.set(ApArticleContent::getContent, dto.getContent()); //相当于set后面的内容
            //相当于set后面的内容
            apArticleContentService.update(updateWrapper);
            //生成静态页面  保存更新都需要
            articleHtmlTasks.createHtml(apArticle, dto.getContent());
            return ResponseResult.okResult(apArticle.getId());
        } else {
            //新增操作
            ApArticle apArticle = new ApArticle();
            BeanUtils.copyProperties(dto, apArticle);
            apArticle.setCreatedTime(new Date());
            apArticle.setId(null);
            //新增文章的信息
            this.save(apArticle);
            //新增文章的内容
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContent.setContent(dto.getContent());
            apArticleContentService.save(apArticleContent);
            //生成静态页面  保存更新都需要
            articleHtmlTasks.createHtml(apArticle, dto.getContent());
            return ResponseResult.okResult(apArticle.getId());
        }
    }

    @Override
    public ResponseResult load(ArticleHomeDto dto, Integer type) {
        //判断是否在首页加载数据库  如果是首页   先从redis 中加载数据
        if (dto.getLoaddir()==1) { //Loaddir加载目录
            //先从redis中获取资源    按分值从大到小排列
            String key = "hot_article_first_page_" + dto.getChannelId();
            long start = 0;
            long end = dto.getSize() - 1;   //结束时需要长度减一
            Set<String> strings = redisTemplate.boundZSetOps(key).reverseRange(start, end); // reverseRange根据分值降序查询
            List<ApArticle> articles = new ArrayList<>();
            for (String string : strings) {
                ApArticle article = JSON.parseObject(string, ApArticle.class);
                articles.add(article);
            }
            if (articles.size() >= dto.getSize()) {   //如果需要的数据比获取到的数据多
                //则证明当前获取到的热点数据 不足  需要进入数据库查询
                return ResponseResult.okResult(articles);
            }
        }
        //在controller 中定义的 0 是最新  1 是加载更多
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        //查询条件  频道id 如果是0  就是查询所有频道
        if (dto.getChannelId() != 0) {
            queryWrapper.eq(ApArticle::getChannelId, dto.getChannelId());
        }
        //发布时间大于Dto中的MaxTime 则证明是 最新文章
        if (type == 0) {
            queryWrapper.gt(ApArticle::getPublishTime, dto.getMaxTime());  //大于
            // 0 代表是 初次加载或者加载更新 发布时间 maxTime
            //1   代表是 加载更多  发布时间 MinTime
        } else {
            queryWrapper.lt(ApArticle::getPublishTime, dto.getMinTime()); //小与
        }
        queryWrapper.eq(ApArticle::getIsDelete, false);  //判断是否删除
        queryWrapper.eq(ApArticle::getIsDown, false); //判断是否下架
        //根据发布时间降序查询
        queryWrapper.orderByDesc(ApArticle::getPublishTime);
        //分页查询
        IPage<ApArticle> page = new Page<>(1, dto.getSize());
        IPage<ApArticle> page1 = this.page(page, queryWrapper);
        return ResponseResult.okResult(page1.getRecords());
    }

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        //根据 id 查询 ap_apArticle
        ApArticle apArticle = this.getById(dto.getArticleId());
        if (apArticle == null) {
            ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //根据 id 查询 ap_apArticle_content
        LambdaQueryWrapper<ApArticleContent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApArticleContent::getArticleId, dto.getArticleId());
        ApArticleContent apArticleContent = apArticleContentService.getOne(queryWrapper);
        //在哪找前端的需求个事返回数据
        Map<String, Object> map = new HashMap<>();
        map.put("config", apArticle);
        map.put("content", apArticleContent);
        //返回通用的响应
        return ResponseResult.okResult(map);
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void compute() {
        //获取今天时间和前300 天的时间
        LambdaQueryWrapper<ApArticle> queryWrapper = Wrappers.lambdaQuery();
        //使用joda-time中的工具类 可以查询200天之前的数据
        Date beforeDate = DateTime.now().minusDays(300).toDate();
        LambdaQueryWrapper<ApArticle> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper.eq(ApArticle::getIsDelete, false);
        queryWrapper.eq(ApArticle::getIsDown, false);
        queryWrapper.ge(ApArticle::getPublishTime, beforeDate);
        List<ApArticle> list = this.list(queryWrapper);
        //先删除数据
        Set<String> keys = redisTemplate.keys("hot_article_first_page_*");
        redisTemplate.delete(keys);
        List<ApArticle> articleList = this.list(queryWrapper);
        for (ApArticle article : articleList) {
            //计算分值
            int score = computeScore(article);
            ApArticle cache = new ApArticle();
            cache.setId(article.getId());
            cache.setTitle(article.getTitle());
            cache.setImages(article.getImages());
            cache.setAuthorId(article.getAuthorId());
            cache.setAuthorName(article.getAuthorName());
            cache.setLayout(article.getLayout());
            cache.setStaticUrl(article.getStaticUrl());
            String jsonString = JSON.toJSONString(cache);
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(jsonString, score); //0代表是首页上推荐的文章
            redisTemplate.boundZSetOps("hot_article_first_page_" + article.getChannelId()).add(jsonString, score);//再放到各自的频道上推荐的文章
        }

    }

    @Override
    public void updateHotArticle(ArticleStreamMessage message) {

        //       1、重新计算文章的热度值（今天产生的数据*3）
        Long articleId = message.getArticleId();
        ApArticle article = this.getById(articleId);
        int score = computeTodayScore(message);

        ApArticle cache = new ApArticle();

        cache.setId(article.getId());
        cache.setTitle(article.getTitle());
        cache.setImages(article.getImages());
        cache.setAuthorId(article.getAuthorId());
        cache.setAuthorName(article.getAuthorName());
        cache.setLayout(article.getLayout());
        cache.setStaticUrl(article.getStaticUrl());

        //      2、更新redis中的数据
        //        String key = "hot_article_first_page_" + dto.getChannelId();
        Double scoreBefore = redisTemplate.boundZSetOps("hot_article_first_page_0").score(JSON.toJSONString(cache));
        if(scoreBefore!=null){
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(cache),scoreBefore+score);
            redisTemplate.boundZSetOps("hot_article_first_page_"+article.getChannelId()).add(JSON.toJSONString(cache),scoreBefore+score);
        }else{
            redisTemplate.boundZSetOps("hot_article_first_page_0").add(JSON.toJSONString(cache),score);
            redisTemplate.boundZSetOps("hot_article_first_page_"+article.getChannelId()).add(JSON.toJSONString(cache),score);
        }

        //     3、更新mysql中ApArticle中的阅读数、点赞数、评论数、收藏数

        LambdaUpdateWrapper<ApArticle> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApArticle::getId,message.getArticleId());
        updateWrapper.setSql("views=views+"+message.getView());
        updateWrapper.setSql("likes=likes+"+message.getLike());
        updateWrapper.setSql("comment=comment+"+message.getComment());
        updateWrapper.setSql("collection=collection+"+message.getCollect());
        this.update(updateWrapper);

    }

    /**
     * 计算当日的增量分数  当日的操作整体权重*3
     *
     * @param message
     * @return
     */
    private int computeTodayScore(ArticleStreamMessage message) {
        int score = 0;
        score += message.getView() * 1 * 3;
        score += message.getLike() * 3 * 3;
        score += message.getComment() * 5 * 3;
        score += message.getCollect() * 8 * 3;
        return score;
    }

    private int computeScore(ApArticle article) {
        int score = 0;
        // 阅读 +1  点赞 +3  评论 +5  收藏 +8
        if (article.getViews() != null) {
            score += article.getViews() * 1;
        }
        if (article.getLikes() != null) {
            score += article.getLikes() * 3;
        }
        if (article.getComment() != null) {
            score += article.getComment() * 5;
        }
        if (article.getCollection() != null) {
            score += article.getCollection() * 8;
        }
        return score;
    }

}

